package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"sync"
	"time"
)

type Limit struct {
	number  int
	channel chan struct{}
}

// Limit struct 初始化
func New(number int) *Limit {
	return &Limit{
		number:  number,
		channel: make(chan struct{}, number),
	}
}

// Run 方法：创建有限的 go f 函数的 goroutine
func (limit *Limit) Run(f func()) {
	limit.channel <- struct{}{}
	go func() {
		f()
		<-limit.channel
	}()
}

// WaitGroup 对象内部有一个计数器，从0开始
// 有三个方法：Add(), Done(), Wait() 用来控制计数器的数量
var wg = sync.WaitGroup{}

const (
	concurrency = 2 // 控制并发量
)

func main() {
	start := time.Now()
	limit := New(concurrency) // New Limit 控制并发量
	// 接口请求URL
	apiUrl := "https://www.sdk.cn/details/9pPQD6wqK0zE8ozvNy" // 不要使用接口地址测试
	//max := int(math.Pow10(8))                  // 模拟一千万数据
	max := 100 // 先测试5次吧

	for i := 0; i < max; i++ {
		wg.Add(1)
		value := i
		goFunc := func() {
			sta := time.Now()
			fmt.Printf("start func: %d\n", value)

			data, err := Get2(apiUrl)
			if err != nil {
				fmt.Println(err)
				return
			}
			// 其它逻辑代码...
			fmt.Println(string(data)[200:400])
			fmt.Printf("num: %d, time：%.5fs\n", value, time.Now().Sub(sta).Seconds())
			//time.Sleep(time.Millisecond * 800)
			wg.Done()
		}
		limit.Run(goFunc)
	}

	// 阻塞代码防止退出
	wg.Wait()

	fmt.Printf("耗时: %.6fs\n", time.Now().Sub(start).Seconds())
}

// Get 方式发起网络请求
func Get2(apiURL string) (rs []byte, err error) {
	resp, err := http.Get(apiURL)
	if err != nil {
		fmt.Println("err:", err)
		return nil, err
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}
